/*
 * Ext GWT 2.2.5 - Ext for GWT
 * Copyright(c) 2007-2010, Ext JS, LLC.
 * licensing@extjs.com
 * 
 * http://extjs.com/license
 */
package com.extjs.gxt.ui.client.widget.menu;

import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.aria.FocusFrame;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.EventType;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.MenuEvent;
import com.extjs.gxt.ui.client.util.KeyNav;
import com.extjs.gxt.ui.client.widget.Container;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Accessibility;

/**
 * A horizontal menu bar.
 */
public class MenuBar extends Container<MenuBarItem> {

	protected MenuBarItem active;
	private boolean autoSelect = true;
	private Listener<MenuEvent> listener;

	public MenuBar() {
		baseStyle = "x-menubar";
		enableLayout = true;
		attachChildren = false;

		listener = new Listener<MenuEvent>() {

			@Override
			public void handleEvent(MenuEvent be) {
				EventType type = be.getType();
				if (type == Events.Hide) {
					autoSelect = false;
					focus();
					autoSelect = true;
					if (active != null) active.expanded = false;
				} else if (type == Events.AutoHide) {

					autoSelect = false;
					focus();
					autoSelect = true;
					if (active != null) active.expanded = false;
				} else if (type == Events.Maximize) {
					int index = indexOf(active);
					index = index != getItemCount() - 1 ? index + 1 : 0;
					MenuBarItem item = getItem(index);
					setActiveItem(item, true);
				} else if (type == Events.Minimize) {
					int index = indexOf(active);
					index = index > 0 ? index - 1 : getItemCount() - 1;
					MenuBarItem item = getItem(index);
					setActiveItem(item, true);
				}
			}
		};

	}

	@Override
	public boolean add(MenuBarItem item) {
		return super.add(item);
	}

	@Override
	public boolean insert(MenuBarItem item, int index) {
		return super.insert(item, index);
	}

	@Override
	public void onComponentEvent(ComponentEvent ce) {
		super.onComponentEvent(ce);

		int type = ce.getEventTypeInt();
		switch (type) {
			case Event.ONMOUSEOVER:
				onMouseOver(ce);
				break;
			case Event.ONMOUSEOUT:
				onMouseOut(ce);
				break;
			case Event.ONCLICK:
				onClick(ce);
				break;
			case Event.ONFOCUS:
				if (autoSelect && active == null && getItemCount() > 0) {
					setActiveItem(getItem(0), false);
				}
				break;
			case Event.ONBLUR:
				if (active != null && !active.expanded) {
					onDeactivate(active);
				}
				break;
		}
	}

	@Override
	public boolean remove(MenuBarItem item) {
		return super.remove(item);
	}

	/**
	 * Sets the active item.
	 * 
	 * @param item the item to activate
	 * @param expand true to expand the item's menu
	 */
	public void setActiveItem(final MenuBarItem item, boolean expand) {
		if (active != item) {
			if (active != null) {
				onDeactivate(active);
			}
			onActivate(item);

			if (GXT.isFocusManagerEnabled()) {
				FocusFrame.get().frame(active);
			}

			if (expand) {
				expand(item, true);
			}
		}
	}

	/**
	 * Toggles the given item.
	 * 
	 * @param item the item to toggle
	 */
	public void toggle(MenuBarItem item) {
		if (item == active) {
			if (item.expanded) {
				collapse(item);
			} else {
				expand(item, false);
			}
		} else {
			setActiveItem(item, true);
		}
	}

	protected void collapse(MenuBarItem item) {
		item.menu.hide();
		item.expanded = false;
	}

	protected void expand(MenuBarItem item, boolean selectFirst) {
		item.menu.setFocusOnShow(false);
		if (LocaleInfo.getCurrentLocale().isRTL())
			item.menu.show(item.getElement(), "tr-br", new int[] {0, 1});
		else
			item.menu.show(item.getElement(), "tl-bl", new int[] {0, 1});
		item.expanded = true;
		if (item.menu.getItemCount() > 0 && selectFirst) {
			item.menu.setActiveItem(item.menu.getItem(0), false);
		}
	}

	protected void onActivate(MenuBarItem item) {
		active = item;
		Accessibility.setState(getElement(), "aria-activedescendant", item.getId());
		item.addStyleName(item.getBaseStyle() + "-active");
		item.addStyleName(item.getBaseStyle() + "-over");
	}

	protected void onDeactivate(MenuBarItem item) {
		if (item.expanded) {
			item.menu.hide();
			item.expanded = false;
		}

		item.removeStyleName(item.getBaseStyle() + "-active");
		item.removeStyleName(item.getBaseStyle() + "-over");
		if (GXT.isFocusManagerEnabled()) {
			FocusFrame.get().unframe();
		}
		if (active == item) {
			active = null;
		}
	}

	@Override
	protected void onInsert(final MenuBarItem item, int index) {
		super.onInsert(item, index);

		item.menu.addListener(Events.Hide, listener);
		item.menu.addListener(Events.AutoHide, listener);
		item.menu.addListener(Events.Maximize, listener);
		item.menu.addListener(Events.Minimize, listener);
	}

	@Override
	protected void onRemove(MenuBarItem item) {
		super.onRemove(item);
		item.menu.removeListener(Events.Hide, listener);
		item.menu.removeListener(Events.AutoHide, listener);
		item.menu.removeListener(Events.Maximize, listener);
		item.menu.removeListener(Events.Minimize, listener);
	}

	protected void onKeyPress(ComponentEvent ce) {
		switch (ce.getKeyCode()) {
			case KeyCodes.KEY_DOWN:
				onDown(ce);
				break;
			case KeyCodes.KEY_LEFT:
				onLeft(ce);
				break;
			case KeyCodes.KEY_RIGHT:
				onRight(ce);
				break;
		}
	}

	protected void onLeft(ComponentEvent ce) {
		if (active != null && getItemCount() > 1) {
			int idx = indexOf(active);
			idx = idx != 0 ? idx - 1 : getItemCount() - 1;
			MenuBarItem item = getItem(idx);
			setActiveItem(item, item.expanded);
		}
	}

	protected void onMouseOut(ComponentEvent ce) {
		EventTarget eT = ce.getEvent().getRelatedEventTarget();
		if ((eT == null || (Element.is(eT) && findItem((Element) Element.as(eT)) == null)) && active != null && !active.expanded) {
			onDeactivate(active);
		}
	}

	protected void onMouseOver(ComponentEvent ce) {
		MenuBarItem item = findItem(ce.getTarget());
		if (item != null && item != active) {
			setActiveItem(item, active != null && active.expanded);
		}
	}

	@Override
	protected void onRender(Element target, int index) {
		super.onRender(target, index);
		setElement(DOM.createDiv(), target, index);

		el().setTabIndex(-1);
		el().setElementAttribute("hideFocus", "true");

		if (GXT.isAriaEnabled()) {
			Accessibility.setRole(getElement(), Accessibility.ROLE_MENUBAR);
			Accessibility.setState(getElement(), "aria-hidden", "false");
		}

		new KeyNav<ComponentEvent>(this) {
			@Override
			public void onKeyPress(ComponentEvent ce) {
				MenuBar.this.onKeyPress(ce);
			}
		};

		layout();

		sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.ONFOCUS | Event.ONBLUR);
	}

	@Override
	protected void onResize(int width, int height) {

	}

	protected void onRight(ComponentEvent ce) {
		if (active != null && getItemCount() > 1) {
			int idx = indexOf(active);
			idx = idx != getItemCount() - 1 ? idx + 1 : 0;
			MenuBarItem item = getItem(idx);
			setActiveItem(item, item.expanded);
		}
	}

	private void onClick(ComponentEvent ce) {
		ce.stopEvent();
		MenuBarItem item = findItem(ce.getTarget());
		if (item != null) {
			toggle(item);
		}
	}

	private void onDown(ComponentEvent ce) {
		if (active != null && getItemCount() > 0) {
			ce.stopEvent();
			if (active.expanded) {
				active.menu.focus();
				active.menu.setActiveItem(active.menu.getItem(0), false);
			} else {
				expand(active, true);
			}
		}
	}
}